/* PassivePostiveTCP.cpp */

#include "PassivePostiveTCP.h"
#include "err_and_log.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#ifndef  INADDR_NONE
#define  INADDR_NONE  0xffffffff
#endif  /* INADDR_NONE */

SOCKET passivesock(const char *, const char *, int);

SOCKET	connectsock(const char *host, const char *service, const char *transport , struct sockaddr_in *);

/*------------------------------------------------------------------------
 * passiveTCP - create a passive socket for use in a TCP server
 *------------------------------------------------------------------------
 */
SOCKET	passiveTCP(const char *service, int qlen)
{
    return passivesock(service, "tcp", qlen);
}

/*------------------------------------------------------------------------
 * connectTCP - connect to a specified TCP service on a specified host
 *------------------------------------------------------------------------
 */
SOCKET	postiveTCP(const char *host, const char *service )
{
    return connectsock( host, service, "tcp", NULL);
}

SOCKET postiveTCP_with_addr(struct sockaddr_in *remote_addr)
{
	return connectsock( "localhost", "ftp", "tcp", remote_addr);
}

/*------------------------------------------------------------------------
 * passivesock - allocate & bind a server socket using TCP or UDP
 *------------------------------------------------------------------------
 */
SOCKET
passivesock(const char *service, const char *transport, int qlen)
{
    u_short portbase = 0; /* port base, for test servers */
    struct servent *pse; /* pointer to service information entry  */
    struct protoent *ppe;  /* pointer to protocol information entry*/
    struct sockaddr_in sin; /* an Internet endpoint address */
    SOCKET s;  /* socket descriptor */
    int type; /* socket type (SOCK_STREAM, SOCK_DGRAM)*/
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY;
    /* Map service name to port number */
    if (( pse = getservbyname(service, transport)) != NULL )
        sin.sin_port = htons(ntohs((u_short)pse->s_port)
                + portbase);
    else if ( (sin.sin_port = htons((u_short)atoi(service))) == 0 )
	
        errmsg("can't get \"%s\" service entry\n", service);
		
	/* Map protocol name to protocol number */
    if ( (ppe = getprotobyname(transport)) == 0)
        errmsg("can't get \"%s\" protocol entry\n", transport);
    /* Use protocol to choose a socket type */
    if (strcmp(transport, "udp") == 0)
        type = SOCK_DGRAM;
    else
        type = SOCK_STREAM;
    /* Allocate a socket */
    s = socket(PF_INET, type, ppe->p_proto);
    if (s == INVALID_SOCKET)
        errmsg("can't create socket: %d\n", GetLastError());
    /* Bind the socket */
    if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR)
        errmsg("can't bind to %s port: %d\n", service,
                GetLastError());
    if (type == SOCK_STREAM && listen(s, qlen) == SOCKET_ERROR)
	{
        errmsg("can't listen on %s port: %d\n", service, GetLastError());
		return 0;
	}
    return s;
}

/*------------------------------------------------------------------------
 * connectsock - allocate & connect a socket using TCP or UDP
 *------------------------------------------------------------------------
 */
SOCKET
connectsock(const char *host, const char *service, const char *transport, struct sockaddr_in *remote_addr)
{
    struct hostent *phe; /* pointer to host information entry  */
    struct servent *pse; /* pointer to service information entry  */
    struct protoent *ppe;  /* pointer to protocol information entry*/
    struct sockaddr_in sin; /* an Internet endpoint address */
    int s, type;  /* socket descriptor and socket type  */
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    /* Map service name to port number */
    if (( pse = getservbyname(service, transport)) != NULL )
        sin.sin_port = pse->s_port;
    else if ( (sin.sin_port = htons((u_short)atoi(service))) == 0 )
	{
     	errmsg("can't get \"%s\" service entry\n", service);
		return 0;
    }
	/* Map host name to IP address, allowing for dotted decimal */
    if ( (phe = gethostbyname(host))!=NULL )
        memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
    else if ( (sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE)
	{
        errmsg("can't get \"%s\" host entry\n", host);
		return 0;
    }/* Map protocol name to protocol number */

	if(remote_addr != NULL)
		memcpy(&sin, remote_addr, sizeof(struct sockaddr_in));

    if ( (ppe = getprotobyname(transport)) == 0)
	{
        errmsg("can't get \"%s\" protocol entry\n", transport);
		return 0;
	}
    /* Use protocol to choose a socket type */
    if (strcmp(transport, "udp") == 0)
        type = SOCK_DGRAM;
    else
        type = SOCK_STREAM;
    /* Allocate a socket */
    s = socket(PF_INET, type, ppe->p_proto);
    if (s == INVALID_SOCKET)
	{
        errexit("can't create socket: %d\n", GetLastError());
		return 0;
	}
    /* Connect the socket */
    if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) == SOCKET_ERROR)
	{
        errmsg("can't connect to %s.%s: %d\n", host, service,
                GetLastError());
		return 0;
	}
    return s;
}